package com.example.demo.service.impl;

import com.example.demo.config.RedisException;
import com.example.demo.entity.Talk;
import com.example.demo.service.LikeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * bitmap 实现 点赞/取消赞 操作
 *
 * @author Code Farmer
 * @date 2020/4/22 15:57
 */
@Service
public class LikeServiceImpl implements LikeService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired(required = false)
    private ValueOperations<String, String> valueOperations;

    @Override
    public boolean likeAndCancelLike(Talk talk) throws RedisException {
        return Optional
                .ofNullable(valueOperations.setBit(talk.getId(), talk.getLikeUserId(), talk.isStatus()))
                .orElseThrow(() -> new RedisException("likeAndCancelLike error"));
    }

    @Override
    public long getLikeCount(Talk talk) throws RedisException {
        return this.bitCount(talk.getId());
    }

    @Override
    public boolean isLike(Talk talk) throws RedisException {
        return Optional
                .ofNullable(valueOperations.getBit(talk.getId(), talk.getLikeUserId()))
                .orElseThrow(() -> new RedisException("isLike error"));
    }

    private long bitCount(String key) throws RedisException {
        return Optional
                .ofNullable(redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes())))
                .orElseThrow(() -> new RedisException("bitCount error"));
    }

    private Long bitCount(String key, int start, int end) {
        return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes(), start, end));
    }

    private Long bitOp(RedisStringCommands.BitOperation op, String saveKey, String... desKey) {
        byte[][] bytes = new byte[desKey.length][];
        for (int i = 0; i < desKey.length; i++) {
            bytes[i] = desKey[i].getBytes();
        }
        return redisTemplate.execute((RedisCallback<Long>) con -> con.bitOp(op, saveKey.getBytes(), bytes));
    }

}
